#include "EventManager.h"
#include <string.h>

#define TAG "EventManager"
//#include <stdio.h>
#include "elog.h"
#define NANO_LOGD(tag,...)  elog_d(TAG,__VA_ARGS__)//printf("[debug]%s:",tag);printf(__VA_ARGS__);printf("\r\n")
#define NANO_LOGI(tag,...)  elog_i(TAG,__VA_ARGS__)//printf("[info]%s:",tag);printf(__VA_ARGS__);printf("\r\n")
#define NANO_LOGW(tag,...)  elog_w(TAG,__VA_ARGS__)//printf("[warn]%s:",tag);printf(__VA_ARGS__);printf("\r\n")
#define NANO_LOGE(tag,...)  elog_e(TAG,__VA_ARGS__)//printf("[error]%s:",tag);printf(__VA_ARGS__);printf("\r\n")
#define NANO_HEXDUMP(tag,buf,len)   elog_hexdump(TAG,16,buf,len)

std::list<EventManager*> EventManager::_eventManagerList;
std::mutex EventManager::_eventManagerListLock;

EventManager::EventManager( const char* name )
{
    _eventManagerListLock.lock();

    _eventManagerList.push_back( this );
    _name = name;
    _eventIdNodeListLock = new std::mutex;
    _eventIdNodeListLock->unlock();
    _eventListLock = new std::mutex;
    _eventListLock->unlock();
    _thread = NULL;
    _threadNotify = new std::mutex;
    _threadNotify->lock();
    _threadDestroyedNotify = new std::mutex;
    _threadDestroyedNotify->lock();

    _eventManagerListLock.unlock();

    NANO_LOGI( TAG , "Event manager:\"%s\" created." , name );
}

EventManager::~EventManager()
{
    _eventManagerListLock.lock();
    _eventManagerList.remove(this);
    _eventManagerListLock.unlock();

    //停止线程
    stop();
    delete _threadDestroyedNotify;
    delete _threadNotify;

    //释放资源
    _eventIdNodeListLock->lock();
    _eventListLock->lock();
    for( auto it = _eventIdNodeList.begin() ; it != _eventIdNodeList.end() ; it++ )
    {
        for( auto cbIt = (*it)->callbackNodeList.begin() ; cbIt != (*it)->callbackNodeList.end() ; cbIt++ )
        {
            delete (*cbIt);
            (*it)->callbackNodeList.remove( *cbIt );
        }
        delete (*it);
        _eventIdNodeList.remove(*it);
    }

    delete _eventIdNodeListLock;
    delete _eventListLock;
}

bool EventManager::isActive()
{
    return _thread ? true : false;
}

bool EventManager::start()
{
    if( isActive() )
    {
        return true;
    }

    _thread = new std::thread(_managerThread,this);
    NANO_LOGI( TAG , "Event manager:\"%s\" start." , _name );

    return true;
}

bool EventManager::stop()
{
    if( !isActive() )
    {
        return true;
    }

    _threadDestroyedNotify->unlock();
    _threadNotify->unlock();
    _thread->join();

    return true;
}

bool EventManager::registerEventCallback( const char* callback_name , EventCallback callback , int eventId , void* userCtx)
{
    CallbackNode* cbNode = new CallbackNode;
    cbNode->callback_name = callback_name;
    cbNode->cb = callback;
    cbNode->userCtx = userCtx;

    _eventIdNodeListLock->lock();

    EventIdNode* eventNode = _findEventNode(eventId,this->_eventIdNodeList);
    if( eventNode == NULL )
    {
        eventNode = _addEventNode(eventId,this->_eventIdNodeList);
    }

    eventNode->callbackNodeList.push_back(cbNode);

    _eventIdNodeListLock->unlock();

    return true;
}

bool EventManager::registerEventCallback(const char* managerName, const char* callback_name ,EventCallback callback , int eventId , void* userCtx)
{
    EventManager* manager = getManagerInstance( managerName );
    if( manager != NULL )
    {
        return manager->registerEventCallback( callback_name , callback , eventId , userCtx );
    }
    return false;
}

bool EventManager::emitEvent( int eventId )
{
    _eventListLock->lock();
    _eventList.push_back(eventId);
    _eventListLock->unlock();

    _threadNotify->unlock();
    return true;
}

bool EventManager::emitEvent(const char* managerName , int eventId )
{
    _eventManagerListLock.lock();
    EventManager* manager = getManagerInstance( managerName );
    if( manager != NULL )
    {
        manager->emitEvent( eventId );
        _eventManagerListLock.unlock();
        return true;
    }
    _eventManagerListLock.unlock();
    NANO_LOGI(TAG,"Emit event:%d failed,can`t find manager:%s." , eventId , managerName );
    return false;
}

EventManager::EventIdNode* EventManager::_addEventNode(int eventId , std::list<EventIdNode*>& nodeList)
{
    EventIdNode* eventIdNode = _findEventNode(eventId,nodeList);
    if( eventIdNode != NULL )
    {
        return eventIdNode;
    }

    eventIdNode = new EventIdNode;
    eventIdNode->evendId = eventId;
    eventIdNode->callbackNodeList.clear();

    return eventIdNode;
}

EventManager::EventIdNode* EventManager::_findEventNode(int eventId , std::list<EventIdNode*>& nodeList)
{
    for( auto it = nodeList.begin() ; it != nodeList.end() ; it++ )
    {
        if( (*it)->evendId == eventId )
        {
            return (*it);
        }
    }
    return NULL;
}

void EventManager::_managerThread()
{
    int eventId;
    NANO_LOGI(TAG,"manager:%s thread start." , this->_name );
    while(1)
    {
        this->_threadNotify->lock();

        if( this->_threadDestroyedNotify->try_lock() )
        {
            NANO_LOGI(TAG,"manager:%s thread return." , this->_name );
            this->_threadDestroyedNotify->unlock();
            this->_threadNotify->unlock();
            return;
        }

        this->_eventListLock->lock();
        this->_eventIdNodeListLock->lock();
        for( auto evt_it = _eventList.begin() ; evt_it != _eventList.end(); evt_it++)
        {
            eventId = *evt_it;
            EventIdNode* eventNode = _findEventNode( eventId , this->_eventIdNodeList );
            if( eventNode )
            {
                for( auto it = eventNode->callbackNodeList.begin() ; it != eventNode->callbackNodeList.end() ; it++ )
                {
                    NANO_LOGW(TAG,"manager:%s event(id:%d) happen,call back handler:\"%s\"." , this->_name , eventId , (*it)->callback_name );
                    (*it)->cb( eventId , (*it)->userCtx );
                }
            }
            else
            {
                NANO_LOGW(TAG,"manager:%s don`t have evevt:%d handler." , this->_name , eventId );
            }
        }
        _eventList.clear();
        this->_eventIdNodeListLock->unlock();
        this->_eventListLock->unlock();

    }
}

EventManager* EventManager::getManagerInstance(const char* managerName)
{
    for( auto it = _eventManagerList.begin() ; it != _eventManagerList.end() ; it++ )
    {
        if( !strcmp( managerName , (*it)->_name ) )
        {
            return (*it);
        }
    }
    return NULL;
}
